#include "modules/mod_sampler2d.frag"

/************
 * uniforms *
 ************/
uniform sampler2D colorTex1_;
uniform sampler2D vectorField2D_;
uniform sampler2D importanceField_;

uniform vec2 viewportDimensions_;

uniform vec2 flowDimensions_;
uniform int kernelSize_;

uniform bool mouseDown_;
uniform vec2 mouseCoord_;

/************************************************************************
 * Some static variables I declare here for rapid prototyping of shader *
 ************************************************************************/
float LENS_SIZE = min(viewportDimensions_.x, viewportDimensions_.y) / 4;
float SCALE_FACTOR = 1.00;
float ENLARGED_SIZE = LENS_SIZE * SCALE_FACTOR;
float RIDGE_SIZE = LENS_SIZE * 0.02;
vec4 RIDGE_COLOUR = vec4(0.0, 1.0, 0.5, 1.0);

/********************
 * Shader functions *
 ********************/
vec4 get_ridge_colour(float distn){
	float midridge = RIDGE_SIZE * 0.5;
	float distridge = LENS_SIZE - distn;
	float midridgedist = 1.0 - (abs(midridge - distridge) / midridge);
	return RIDGE_COLOUR * midridgedist;
}

/*****************
 * LIC algorithm *
 *****************/
vec4 performLIC(sampler2D inputTexture, sampler2D flow, vec2 coord){

	vec2 tc = coord;
	vec4 res = texture(inputTexture, clamp(tc/viewportDimensions_, 0.0, 1.0));

	vec2 tempv;
	int counter = 1;
	int k = kernelSize_;
	// This piece of code assumes vector length as importance measure by using it as a scaling factor
	// for the kernel size. TODO: design a scalar field in C++ that records the importance of the
	// vector field at each point - either discrete (1's and 0's) or continuous on [0,1]
	vec2 vc = (texture(flow, clamp(tc/viewportDimensions_, 0.0, 1.0)).xy * 2) - vec2(1,1);
	float ln = sqrt(vc.x*vc.x + vc.y*vc.y);
	k = int( ceil(kernelSize_ * ln) );

	for(int i=0; i<k; ++i){
		tc += (texture(flow, clamp(tc/viewportDimensions_, 0.0, 1.0)).xy * 2) - vec2(1,1);
		if( tc.x < 0 || tc.x > viewportDimensions_.x
	 	 || tc.y < 0 || tc.y > viewportDimensions_.y ){ break; }
		res += texture(inputTexture, clamp(tc/viewportDimensions_, 0.0, 1.0));
		++counter;
	}
	tc = coord;
	for(int i=0; i<k; ++i){
		tc -= (texture(flow, clamp(tc/viewportDimensions_, 0.0, 1.0)).xy * 2) - vec2(1,1);
		if( tc.x < 0 || tc.x > viewportDimensions_.x
	 	 || tc.y < 0 || tc.y > viewportDimensions_.y ){ break; }
		res += texture(inputTexture, clamp(tc/viewportDimensions_, 0.0, 1.0));
		++counter;
	}
	res /= counter;
	return res;
}

void main() {
    vec2 fragCoord = gl_FragCoord.xy;

    vec4 c = vec4(0.0);

	vec2 middle = vec2(mouseCoord_.x, mouseCoord_.y);
	vec2 ptm = middle - fragCoord;
	
	float dist = sqrt( ptm.x*ptm.x + ptm.y*ptm.y );

	if(dist < LENS_SIZE && mouseDown_){
		if(dist >= LENS_SIZE - RIDGE_SIZE){
			c = get_ridge_colour(dist);
		}else{
			// scale the vector pointing to the middle of the lens to match the enlarged area 
			// proportions, and translate from the lens centre to obtain coordinates in enlarged area
			ptm *= SCALE_FACTOR;
			vec2 lensCoord = middle - ptm;
			c = texture(colorTex1_, clamp(lensCoord/viewportDimensions_, 0.0, 1.0));
		}
	}else{
		c = texture(colorTex1_, clamp(fragCoord/viewportDimensions_, 0.0, 1.0));
		if(texture(importanceField_, clamp(fragCoord/viewportDimensions_, 0.0, 1.0)).x >= 1.0){
			c = performLIC(colorTex1_, vectorField2D_, fragCoord);
		}
	}

	// TODO remove this - it is only to see the shape of the importance field..
	c = .8*c + .2*texture(importanceField_, clamp(fragCoord/viewportDimensions_, 0.0, 1.0)).x;
    FragData0 = c;
    gl_FragDepth = 1.0;
}
